# photo.py
import wx
import util
import logging

class Photo(wx.PyControl):
    def __init__(self, parent, photoid, img, scale, id=wx.ID_ANY, label="", pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.NO_BORDER, validator=wx.DefaultValidator,
                 name="CustomCheckBox"):
        '''
        Default class constructor.

        @param parent: Parent window. Must not be None.
        @param id: CustomCheckBox identifier. A value of -1 indicates a default value.
        @param label: Text to be displayed next to the checkbox.
        @param pos: CustomCheckBox position. If the position (-1, -1) is specified
                    then a default position is chosen.
        @param size: CustomCheckBox size. If the default size (-1, -1) is specified
                     then a default size is chosen.
        @param style: not used in this demo, CustomCheckBox has only 2 state
        @param validator: Window validator.
        @param name: Window name.
        '''

        # Ok, let's see why we have used wx.PyControl instead of wx.Control.
        # Basically, wx.PyControl is just like its wxWidgets counterparts
        # except that it allows some of the more common C++ virtual method
        # to be overridden in Python derived class. For CustomCheckBox, we
        # basically need to override DoGetBestSize and AcceptsFocusFromKeyboard
        
        wx.PyControl.__init__(self, parent, id, pos, size, style, validator, name)

        # Initialize our cool bitmaps
        self.img = img
        self.scale = scale
        self.photoid = photoid

        # Set the spacing between the check bitmap and the label to 3 by default.
        # This can be changed using SetSpacing later.
        self._spacing = 3
        self._hasFocus = False

        # Ok, set the wx.PyControl label, its initial size (formerly known an
        # SetBestFittingSize), and inherit the attributes from the standard
        # wx.CheckBox
        self.SetLabel(label)
        self.SetInitialSize(size)
        self.InheritAttributes()

        # Bind the events related to our control: first of all, we use a
        # combination of wx.BufferedPaintDC and an empty handler for
        # wx.EVT_ERASE_BACKGROUND (see later) to reduce flicker
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

        # Then we want to monitor user clicks, so that we can switch our
        # state between checked and unchecked
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseClick)
        if wx.Platform == '__WXMSW__':
            # MSW Sometimes does strange things...
            self.Bind(wx.EVT_LEFT_DCLICK,  self.OnMouseClick)

        # We want also to react to keyboard keys, namely the
        # space bar that can toggle our checked state
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        # Then, we react to focus event, because we want to draw a small
        # dotted rectangle around the text if we have focus
        # This might be improved!!!
        self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
        
    @property
    def bitmap(self):
        thumb = util.fancy_thumbnail(self.img, (self.scale * 120, self.scale * 120))
        return util.pil_to_bitmap(thumb)
        
    def set_scale(self, scale):
        self.scale = scale
        # self.SetSize(self.DoGetBestSize())
        # self.Refresh()

    def SetLabel(self, label):
        '''
        Sets the CustomCheckBox text label and updates the control's size to
        exactly fit the label plus the bitmap.
        '''
        wx.PyControl.SetLabel(self, label)

        # The text label has changed, so we must recalculate our best size
        # and refresh ourselves.
        self.InvalidateBestSize()
        self.Refresh()

    def SetFont(self, font):
        '''
        Sets the CustomCheckBox text font and updates the control's size to
        exactly fit the label plus the bitmap.
        '''
        
        wx.PyControl.SetFont(self, font)

        # The font for text label has changed, so we must recalculate our best
        # size and refresh ourselves.        
        self.InvalidateBestSize()
        self.Refresh()


    def DoGetBestSize(self):
        '''
        Overridden base class virtual.  Determines the best size of the control
        based on the label size, the bitmap size and the current font.
        '''

        # Retrieve our properties: the text label, the font and the check
        # bitmap
        label = self.GetLabel()
        font = self.GetFont()
        bitmap = self.bitmap

        if not font:
            # No font defined? So use the default GUI font provided by the system
            font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)

        # Set up a wx.ClientDC. When you don't have a dc available (almost
        # always you don't have it if you are not inside a wx.EVT_PAINT event),
        # use a wx.ClientDC (or a wx.MemoryDC) to measure text extents
        dc = wx.ClientDC(self)
        dc.SetFont(font)

        # Measure our label        
        textWidth, textHeight = dc.GetTextExtent(label)
        
        # Retrieve the check bitmap dimensions
        bitmapWidth, bitmapHeight = bitmap.GetWidth(), bitmap.GetHeight()
        
        # Get the spacing between the check bitmap and the text
        spacing = self.GetSpacing()

        # Ok, we're almost done: the total width of the control is simply
        # the sum of the bitmap width, the spacing and the text width,
        # while the height is the maximum value between the text width and
        # the bitmap width
        totalWidth = bitmapWidth + spacing + textWidth
        totalHeight = max(textHeight, bitmapHeight)
                
        best = wx.Size(totalWidth, totalHeight)

        # Cache the best size so it doesn't need to be calculated again,
        # at least until some properties of the window change
        self.CacheBestSize(best)

        return best


    def AcceptsFocusFromKeyboard(self):
        '''Overridden base class virtual.'''
        # We can accept focus from keyboard, obviously
        return True

    def AcceptsFocus(self):
        '''Overridden base class virtual.'''
        # It seems to me that wx.CheckBox does not accept focus with mouse
        # but please correct me if I am wrong!
        return False        


    def HasFocus(self):
        '''Returns whether or not we have the focus.'''

        # We just returns the _hasFocus property that has been set in the
        # wx.EVT_SET_FOCUS and wx.EVT_KILL_FOCUS event handlers.
        return self._hasFocus


    def SetForegroundColour(self, colour):
        '''Overridden base class virtual.'''

        wx.PyControl.SetForegroundColour(self, colour)

        # We have to re-initialize the focus indicator per colour as it should
        # always be the same as the foreground colour
        self.Refresh()


    def SetBackgroundColour(self, colour):
        '''Overridden base class virtual.'''

        wx.PyControl.SetBackgroundColour(self, colour)

        # We have to refresh ourselves
        self.Refresh()

    def GetDefaultAttributes(self):
        '''
        Overridden base class virtual.  By default we should use
        the same font/colour attributes as the native wx.CheckBox.
        '''
        
        return wx.CheckBox.GetClassDefaultAttributes()

    def ShouldInheritColours(self):
        '''
        Overridden base class virtual.  If the parent has non-default
        colours then we want this control to inherit them.
        '''
        
        return True

    def SetSpacing(self, spacing):
        '''Sets a new spacing between the check bitmap and the text.'''
        self._spacing = spacing

        # The spacing between the check bitmap and the text has changed,
        # so we must recalculate our best size and refresh ourselves.
        self.InvalidateBestSize()
        self.Refresh()

    def GetSpacing(self):
        '''Returns the spacing between the check bitmap and the text.'''
        return self._spacing

    def OnKeyDown(self, event):
        '''Handles the wx.EVT_KEY_DOWN event for CustomCheckBox.'''
        if event.GetKeyCode() == wx.WXK_SPACE:
            # The spacebar has been pressed: toggle our state
            event.Skip()
            return

        event.Skip()

    def OnSetFocus(self, event):
        '''Handles the wx.EVT_SET_FOCUS event for CustomCheckBox.'''
        self._hasFocus = True

        # We got focus, and we want a dotted rectangle to be painted
        # around the checkbox label, so we refresh ourselves
        self.Refresh()

    def OnKillFocus(self, event):
        '''Handles the wx.EVT_KILL_FOCUS event for CustomCheckBox.'''
        self._hasFocus = False

        # We lost focus, and we want a dotted rectangle to be cleared
        # around the checkbox label, so we refresh ourselves        
        self.Refresh()
        
    def OnPaint(self, event):
        '''Handles the wx.EVT_PAINT event for CustomCheckBox.'''
        # If you want to reduce flicker, a good starting point is to
        # use wx.BufferedPaintDC.
        dc = wx.BufferedPaintDC(self)

        # Is is advisable that you don't overcrowd the OnPaint event
        # (or any other event) with a lot of code, so let's do the
        # actual drawing in the Draw() method, passing the newly
        # initialized wx.BufferedPaintDC
        self.Draw(dc)

    def Draw(self, dc):
        '''
        Actually performs the drawing operations, for the bitmap and
        for the text, positioning them centered vertically.
        '''
        # Get the actual client size of ourselves
        width, height = self.GetClientSize()

        if not width or not height:
            # Nothing to do, we still don't have dimensions!
            return

        # Initialize the wx.BufferedPaintDC, assigning a background
        # colour and a foreground colour (to draw the text)
        backColour = self.GetBackgroundColour()
        backBrush = wx.Brush(backColour, wx.SOLID)
        dc.SetBackground(backBrush)
        dc.Clear()

        dc.SetTextForeground(self.GetForegroundColour())
            
        dc.SetFont(self.GetFont())

        # Get the text label for the checkbox, the associated check bitmap
        # and the spacing between the check bitmap and the text
        label = self.GetLabel()
        bitmap = self.bitmap
        spacing = self.GetSpacing()

        # Measure the text extent and get the check bitmap dimensions
        textWidth, textHeight = dc.GetTextExtent(label)
        bitmapWidth, bitmapHeight = bitmap.GetWidth(), bitmap.GetHeight()

        # Position the bitmap centered vertically
        bitmapXpos = 0
        bitmapYpos = (height - bitmapHeight)/2

        # Position the text centered vertically
        textXpos = bitmapWidth + spacing
        textYpos = (height - textHeight)/2

        # Draw the bitmap on the DC
        dc.DrawBitmap(bitmap, bitmapXpos, bitmapYpos, True)

        # Draw the text
        dc.DrawText(label, textXpos, textYpos)

        # Let's see if we have keyboard focus and, if this is the case,
        # we draw a dotted rectangle around the text (Windows behavior,
        # I don't know on other platforms...)
#        if self.HasFocus():
#            # Yes, we are focused! So, now, use a transparent brush with
#            # a dotted black pen to draw a rectangle around the text
#            dc.SetBrush(wx.TRANSPARENT_BRUSH)
#            dc.SetPen(self._focusIndPen)
#            dc.DrawRectangle(textXpos, textYpos, textWidth, textHeight)

    def OnEraseBackground(self, event):
        '''Handles the wx.EVT_ERASE_BACKGROUND event for CustomCheckBox.'''

        # This is intentionally empty, because we are using the combination
        # of wx.BufferedPaintDC + an empty OnEraseBackground event to
        # reduce flicker
        pass

    def OnMouseClick(self, event):
        '''Handles the wx.EVT_LEFT_DOWN event for CustomCheckBox.'''
        logging.debug('left mouse clicked photo %s' % self.photoid)
        if not self.IsEnabled():
            # Nothing to do, we are disabled
            return

        event.Skip()



